package cn.schoolwow.quickdao.module.database.parent.flow.executor;

import cn.schoolwow.quickdao.domain.QuickDAOConfig;
import cn.schoolwow.quickdao.domain.database.parent.DatabaseType;
import cn.schoolwow.quickdao.domain.entity.SqlLog;
import cn.schoolwow.quickdao.module.database.dml.common.domain.ManipulationOption;
import cn.schoolwow.quickdao.module.database.parent.domain.ResultSetConsumer;
import cn.schoolwow.quickdao.module.database.parent.flow.executor.common.*;
import cn.schoolwow.quickflow.domain.FlowContext;
import cn.schoolwow.quickflow.flow.BusinessFlow;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.*;
import java.util.StringTokenizer;

public class ExecuteUpdateConnectionFlow implements BusinessFlow {
    private Logger logger = LoggerFactory.getLogger(ExecuteUpdateConnectionFlow.class);

    @Override
    public void executeBusinessFlow(FlowContext flowContext) throws Exception {
        flowContext.executeFlowList(new BeforeExecuteFlow());
        flowContext.startFlow(new RealtimePrintStatementCommonFlow())
                .putCurrentCompositeFlowData("sqlLogType", 1)
                .execute();
        int effect = executeUpdate(flowContext);
        flowContext.putData("effect", effect);
    }

    @Override
    public String name() {
        return "执行更新语句";
    }

    private int executeUpdate(FlowContext flowContext) throws SQLException {
        String name = flowContext.checkData("name", String.class);
        String sql = flowContext.checkData("sql", String.class);
        String formatSQL = flowContext.checkData("formatSQL", String.class);

        flowContext.putTemporaryData("startTime", System.currentTimeMillis());
        String executeSQL = sql;
        int indexOfSemicolon = sql.indexOf(";");
        try {
            int effect = 0;
            if (indexOfSemicolon>=0&&indexOfSemicolon!=sql.length()-1) {
                StringTokenizer st = new StringTokenizer(sql, ";");
                while (st.hasMoreTokens()) {
                    executeSQL = st.nextToken();
                    effect += doExecuteUpdate(executeSQL, flowContext);
                }
            } else {
                effect += doExecuteUpdate(executeSQL, flowContext);
            }
            flowContext.startFlow(new InsertSqlLogFlow())
                    .putData("effect", effect)
                    .putTemporaryData("sqlLogType", 1)
                    .execute();
            SqlLog sqlLog = (SqlLog) flowContext.checkData("sqlLog");
            logger.debug("[更新]名称:{},耗时:{}ms,影响行数:{},执行语句:{}", name, sqlLog.getConsumeTime(), effect, formatSQL);

            String content = "[更新]名称:"+name+",耗时:"+sqlLog.getConsumeTime()+"ms,影响行数:"+effect+",执行语句:"+formatSQL+"\r\n";
            flowContext.startFlow(new RecordStatementFlow())
                    .putCurrentCompositeFlowData("content", content)
                    .execute();
            return effect;
        } catch (Exception e) {
            flowContext.startFlow(new InsertSqlLogFlow())
                    .next(new ExecuteFailFlow())
                    .putTemporaryData("exception", e)
                    .putTemporaryData("sqlLogType", 1)
                    .execute();
            throw e;
        }finally {
            flowContext.removeData("parameters");
        }
    }

    private int doExecuteUpdate(String sql, FlowContext flowContext) throws SQLException {
        QuickDAOConfig quickDAOConfig = flowContext.checkInstanceData(QuickDAOConfig.class);
        Connection transactionConnection = flowContext.getData("transactionConnection", Connection.class);

        if(null==transactionConnection){
            try (Connection connection = quickDAOConfig.databaseContext.dataSource.getConnection()){
                return executeConnectionUpdate(sql, connection, flowContext);
            }
        }else{
            return executeConnectionUpdate(sql, transactionConnection, flowContext);
        }
    }

    private int executeConnectionUpdate(String sql, Connection connection, FlowContext flowContext) throws SQLException {
        ManipulationOption manipulationOption = flowContext.getInstanceData(ManipulationOption.class);
        boolean returnGeneratedKeys = null != manipulationOption && manipulationOption.returnGeneratedKeys;

        try (PreparedStatement preparedStatement = returnGeneratedKeys ?
                connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS) :
                connection.prepareStatement(sql)){
            flowContext.startFlow(new SetPrepareStatementParameterFlow())
                    .putTemporaryData("preparedStatement", preparedStatement)
                    .execute();
            int effect = preparedStatement.executeUpdate();
            if (effect>0&&returnGeneratedKeys) {
                setReturnGenerateKeys(preparedStatement, flowContext);
            }
            return effect;
        }
    }

    private void setReturnGenerateKeys(PreparedStatement preparedStatement, FlowContext flowContext) throws SQLException {
        DatabaseType databaseType = flowContext.checkInstanceData(DatabaseType.class);

        switch (databaseType){
            case Oracle:{
                String tableName = (String) flowContext.checkData("tableName");
                flowContext.getQuickFlow().startFlow(new ExecuteQueryConnectionFlow())
                        .putTemporaryData("name", "获取自增id")
                        .putTemporaryData("sql", "select " + tableName + "_seq.currVal from dual")
                        .putReturnData("resultSetConsumer",new ResultSetConsumer() {
                            @Override
                            public void consumeResultSet(ResultSet resultSet) throws Exception {
                                if (resultSet.next()) {
                                    String generateKeys = resultSet.getString(1);
                                    if(null==generateKeys||generateKeys.isEmpty()){
                                        throw new IllegalArgumentException("获取自增id失败!");
                                    }
                                    flowContext.putData("generateKeys", generateKeys);
                                }
                            }
                        })
                        .execute();
            }break;
            default:{
                try (ResultSet resultSet = preparedStatement.getGeneratedKeys();) {
                    if (resultSet.next()) {
                        String generateKeys = resultSet.getString(1);
                        if(null==generateKeys||generateKeys.isEmpty()){
                            throw new IllegalArgumentException("获取自增id失败!");
                        }

                        flowContext.putData("generateKeys", generateKeys);
                    }
                }
            }
        }
    }
}
